package org.study.quartz.quartzmanager.service.impl;

import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.quartz.*;
import org.quartz.impl.JobDetailImpl;
import org.quartz.impl.matchers.GroupMatcher;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import org.springframework.stereotype.Service;
import org.study.quartz.quartzmanager.QuartzIntegrationJobDetail;
import org.study.quartz.quartzmanager.exception.ScheduleManagerException;
import org.study.quartz.quartzmanager.service.SchedulerManagerService;
import org.study.quartz.quartzmanager.vo.ScheduleTaskInfo;
import org.study.quartz.quartzmanager.vo.SchedulerInfo;

import java.util.*;

/***
 * 定时任务管理类实现
 * @author lyf
 */
@Service
public class SchedulerManagerServiceImpl implements SchedulerManagerService, InitializingBean {

    private Logger logger = LoggerFactory.getLogger(SchedulerManagerServiceImpl.class);

    @Autowired
    private SchedulerFactoryBean schedulerFactoryBean;

    /***
     * 定时任务scheduler
     */
    private Scheduler scheduler;

    @Override
    public List<ScheduleTaskInfo> getPlanedScheduleList() {
        return getScheduleList(false);
    }

    @Override
    public List<ScheduleTaskInfo> getRunningScheduleList() {
        return getScheduleList(true);
    }


    @Override
    public boolean pauseSchedule(String triggerName, String group) {
        return pauseOrResumeSchedule(triggerName, group, true);
    }

    @Override
    public boolean resumeSchedule(String triggerName, String group) {
        return pauseOrResumeSchedule(triggerName, group, false);
    }


    @Override
    public void afterPropertiesSet() throws Exception {
        this.scheduler = schedulerFactoryBean.getScheduler();
    }

    @Override
    public boolean pauseAll() {
        return doOperationScheduler((Scheduler scheduler) -> {
            try {
                scheduler.pauseAll();
                return true;
            } catch (Exception e) {
                logger.error(" scheduler pauseAll error", e);
            }
            return false;
        });
    }

    @Override
    public boolean pauseScheduler() {
        /***
         * 暂停调度器操作
         */
        return doOperationScheduler((Scheduler scheduler) -> {
            try {
                if (!scheduler.isInStandbyMode()) {
                    scheduler.standby();
                    return true;
                } else {
                    logger.info("调度器已暂停,无需重复暂停操作");
                    return false;
                }
            } catch (Exception e) {
                logger.error(" scheduler pauseAll error", e);
            }
            return false;
        });

    }

    @Override
    public boolean startScheduler() {
        /***
         * 恢复/启动调度器操作
         */
        return doOperationScheduler((Scheduler scheduler) -> {
            try {
                if (scheduler.isInStandbyMode()) {
                    scheduler.start();
                    return true;
                } else {
                    logger.info("调度器已启动,无需重复启动");
                    return false;
                }
            } catch (Exception e) {
                logger.error(" scheduler pauseAll error", e);
            }
            return false;
        });
    }

    @Override
    public boolean resumeAll() {
        return doOperationScheduler((Scheduler scheduler) -> {
            try {
                scheduler.resumeAll();
                return true;
            } catch (Exception e) {
                logger.error(" scheduler pauseAll error", e);
            }
            return false;
        });
    }

    @Override
    public SchedulerInfo getAllTaskList() {

        if (this.scheduler == null) {
            return null;
        }

        try {
            SchedulerInfo schedulerInfo = new SchedulerInfo();
            schedulerInfo.setSchedulerName(scheduler.getSchedulerName());
            schedulerInfo.setStatus(scheduler.isInStandbyMode() ? 0 : scheduler.isStarted() ? 1 : -1);
            List<ScheduleTaskInfo> runningList = sortTaskList(getRunningScheduleList(scheduler));
            List<ScheduleTaskInfo> planList = sortTaskList(getPlanedScheduleList(scheduler));
            if (CollectionUtils.isNotEmpty(runningList)) {
                schedulerInfo.setRunningTaskList(runningList);
            }
            if (CollectionUtils.isNotEmpty(planList)) {
                schedulerInfo.setPlanTaskList(planList);
            }
            return schedulerInfo;
        } catch (Exception e) {
            logger.error("SchedulerManagerServiceImpl getAllTaskList Error!");
        }
        return null;
    }

    /**
     * 立即执行任务信息
     *
     * @param jobName
     * @param group
     * @return
     */
    @Override
    public boolean runJobNow(String jobName, String group) {
        try {
            if (scheduler.isInStandbyMode()) {
                scheduler.start();
            }
            JobKey jobKey = JobKey.jobKey(jobName, group);
            scheduler.triggerJob(jobKey);
            return true;
        } catch (Exception e) {
            logger.error("SchedulerManagerServiceImpl runJobNow jobName=" + jobName + ",group=" + group + " Error", e);
        }
        return false;
    }


    @Override
    public boolean addJob(String jobName, String jobGroup, String jobBeanName, String executeMethod, String cronExpression) {
        try {
            TriggerKey triggerKey = TriggerKey.triggerKey(jobName, jobGroup);
            CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
            if (null != trigger) {
                throw new SchedulerException("任务已存在,请勿重复添加!");
            }

            /***
             * 类必须已注册到Spring 中了,若是没有注册到Spring中如何处理?
             */
            JobDetail jobDetail = JobBuilder
                    .newJob(QuartzIntegrationJobDetail.class)
                    .withIdentity(jobName, jobGroup)
                    .build();
            ((JobDetailImpl) jobDetail).setDurability(true);


            //校验一下这个bean 是否存在,将要被执行的方法是否存在?

            Map<String, Object> map = new HashMap<>(0xf + 1);
            map.put("targetBeanName", jobBeanName);
            map.put("targetMethod", executeMethod);
            jobDetail.getJobDataMap().putAll(map);

            //任务表达式
            CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(cronExpression);
            trigger = TriggerBuilder
                    .newTrigger()
                    .withIdentity(jobName, jobGroup)
                    .withSchedule(cronScheduleBuilder)
                    .build();
            scheduler.scheduleJob(jobDetail, trigger);
            return true;
        } catch (Exception e) {
            logger.error("SchedulerManagerServiceImpl addJob error", e);
        }
        return false;
    }

    @Override
    public boolean deleteJob(String jobName, String jobGroup) {
        try {
            JobKey jobKey = JobKey.jobKey(jobName, jobGroup);
            return scheduler.deleteJob(jobKey);
        } catch (Exception e) {
            logger.error("SchedulerManagerServiceImpl deleteJob error", e);
        }
        return false;
    }

    /***
     * 获取任务信息
     * @param isRunning
     * @return
     */
    private List<ScheduleTaskInfo> getScheduleList(boolean isRunning) {
        List<ScheduleTaskInfo> taskList = new ArrayList<>();
        List<ScheduleTaskInfo> scheduleTaskList = isRunning ? getRunningScheduleList(scheduler) : getPlanedScheduleList(scheduler);
        if (CollectionUtils.isNotEmpty(scheduleTaskList)) {
            taskList.addAll(scheduleTaskList);
        }
        return sortTaskList(taskList);

    }


    /***
     * 根据调度器获取调度器中对应的计划任务
     * @param scheduler
     * @return
     */
    private List<ScheduleTaskInfo> getPlanedScheduleList(Scheduler scheduler) {
        try {
            GroupMatcher<JobKey> matcher = GroupMatcher.anyGroup();
            Set<JobKey> jobKeys = scheduler.getJobKeys(matcher);
            if (CollectionUtils.isEmpty(jobKeys)) {
                return Collections.emptyList();
            }
            List<ScheduleTaskInfo> list = new ArrayList<>();
            jobKeys.forEach(jobKey -> {
                try {
                    List<? extends Trigger> triggers = scheduler.getTriggersOfJob(jobKey);
                    triggers.forEach(trigger -> {
                        try {
                            ScheduleTaskInfo scheduleTaskInfo = generateScheduleTaskInfo(scheduler, jobKey, trigger);
                            list.add(scheduleTaskInfo);
                        } catch (Exception e) {
                            logger.error("getPlanedScheduleList error", e);
                        }
                    });
                } catch (Exception e) {
                    logger.error("getPlanedScheduleList error", e);
                }

            });
            return list;
        } catch (SchedulerException e) {
            logger.error("查询任务信息异常", e);
            throw new ScheduleManagerException("查询任务信息异常");
        }

    }


    /***
     * 根据调度器获取调度器中对应的运行中的任务信息
     * @param scheduler
     * @return
     */
    private List<ScheduleTaskInfo> getRunningScheduleList(Scheduler scheduler) {
        try {
            List<ScheduleTaskInfo> list = new ArrayList<>();
            List<JobExecutionContext> executingJobs = scheduler.getCurrentlyExecutingJobs();
            executingJobs.forEach(executingJob -> {
                try {
                    JobDetail jobDetail = executingJob.getJobDetail();
                    JobKey jobKey = jobDetail.getKey();
                    Trigger trigger = executingJob.getTrigger();
                    ScheduleTaskInfo scheduleTaskInfo = generateScheduleTaskInfo(scheduler, jobKey, trigger);
                    list.add(scheduleTaskInfo);
                } catch (Exception e) {
                    logger.error("查询任务信息异常", e);
                    throw new ScheduleManagerException("查询任务信息异常");
                }
            });
            return list;
        } catch (SchedulerException e) {
            logger.error("查询任务信息异常", e);
            throw new ScheduleManagerException("查询任务信息异常");
        }
    }


    /***
     * 任务按照时间进行排序操作
     * @param list
     * @return
     */
    private List<ScheduleTaskInfo> sortTaskList(List<ScheduleTaskInfo> list) {
        final Date currentData = new Date();
        if (CollectionUtils.isEmpty(list)) {
            return list;
        }

        Collections.sort(list, (ScheduleTaskInfo o1, ScheduleTaskInfo o2) -> {
            int c = o1.getSchedulerName().compareTo(o2.getSchedulerName());
            if (c == 0) {
                //若时间是当天的按照时间的优先进行排序操作 "yyyy-MM-dd HH:mm:ss"
                try {
                    Date date1 = DateUtils.parseDate(o1.getNextExecuteTime(), "yyyy-MM-dd HH:mm:ss");
                    Date date2 = DateUtils.parseDate(o2.getNextExecuteTime(), "yyyy-MM-dd HH:mm:ss");
                    /***
                     * 两者都大于当前时间,靠近当前时间的排在最前面
                     */
                    if (date1.compareTo(currentData) >= 0 && date2.compareTo(currentData) >= 0) {
                        return date1.compareTo(date2);
                    }
                    return date2.compareTo(date1);
                } catch (Exception e) {
                    logger.error("时间转换异常");
                    return 0;
                }
            } else {
                return c;
            }
        });


        return list;
    }


    /***
     * 启动或者暂停任务信息
     * @param triggerName
     * @param group
     * @param stop
     * @return
     */
    private boolean pauseOrResumeSchedule(String triggerName, String group, boolean stop) {
        try {
            TriggerKey triggerKey = new TriggerKey(triggerName, group);
            if (!scheduler.checkExists(triggerKey)) {
                return false;
            }

            if (stop) {
                scheduler.pauseTrigger(triggerKey);
            } else {
                scheduler.resumeTrigger(triggerKey);
            }
            return true;

        } catch (SchedulerException e) {
            logger.error(stop ? "暂停任务异常" : "恢复任务异常", e);
            throw new ScheduleManagerException(stop ? "暂停任务异常" : "恢复任务异常");
        }
    }


    /***
     * 生成任务显示信息
     * @param scheduler
     * @param jobKey
     * @param trigger
     * @return
     * @throws SchedulerException
     */
    private ScheduleTaskInfo generateScheduleTaskInfo(Scheduler scheduler, JobKey jobKey, Trigger trigger) throws SchedulerException {

        ScheduleTaskInfo scheduleTaskInfo = new ScheduleTaskInfo();
        Date lastExecuteDate = trigger.getPreviousFireTime();
        Date nextExecuteDate = trigger.getNextFireTime();

        scheduleTaskInfo.setSchedulerName(scheduler.getSchedulerName());
        scheduleTaskInfo.setTaskName(jobKey.getName());
        scheduleTaskInfo.setGroupName(jobKey.getGroup());
        scheduleTaskInfo.setTriggerName(trigger.getKey().getName());
        scheduleTaskInfo.setStatusName(scheduler.getTriggerState(trigger.getKey()).name());
        scheduleTaskInfo.setLastExecuteTime(lastExecuteDate == null ? "" : DateFormatUtils.format(lastExecuteDate, "yyyy-MM-dd HH:mm:ss"));
        scheduleTaskInfo.setNextExecuteTime(nextExecuteDate == null ? "" : DateFormatUtils.format(nextExecuteDate, "yyyy-MM-dd HH:mm:ss"));
        if (trigger instanceof CronTrigger) {
            scheduleTaskInfo.setCronExpression(((CronTrigger) trigger).getCronExpression());
        }

        return scheduleTaskInfo;
    }


    /***
     * 对Scheduler 进行操作
     * @return
     */
    private boolean doOperationScheduler(SchedulerOperation scheduleroperation) {
        return scheduleroperation.operation(this.scheduler);
    }


    /***
     * 操作接口
     * @author lyf
     */
    @FunctionalInterface
    interface SchedulerOperation {

        /**
         * 对Scheduler进行操作
         *
         * @param scheduler
         * @return
         */
        boolean operation(Scheduler scheduler);
    }


}
